Executive summary

I want to give you a final gift, so I put together a litte analysis of the performance of the group. In this notebook, I will show how you performed during the course, and what kind of feedback you received from me. Don’t worry, no one is singled out in the analysis, and identities are kept in confidence.

I’m downloading the data directly from google sheets. To protect personal rights, the data is not public ㊙️.

# Read the files directly from google sheets and add the number of the evaluation (as character)
eval1 <-
    gs_title("assignment1_evaluations", verbose = FALSE) %>% 
    gs_read(1, verbose = FALSE) %>% 
    mutate(assignment = "1")
eval2 <-
    gs_title("assignment2_evaluations", verbose = FALSE) %>% 
    gs_read(1, verbose = FALSE) %>% 
    mutate(assignment = "2")
eval3 <-
    gs_title("assignment3_evaluations", verbose = FALSE) %>% 
    gs_read(1, verbose = FALSE) %>% 
    mutate(assignment = "3")
# Bind the three evaluations together
evaluations <- 
    bind_rows(eval1, eval2, eval3) %>% 
    mutate( assignment = as.factor(assignment),
            submitted = if_else(points != 0, TRUE, FALSE),
            rel_points = points/max_points)

Sorry, I can’t give you the data! 😢

Percentage of submitted assignments

First, let’s see how many of you were busy bees and submitted assignments. It looks like most of you who submitted an assignment kept this good habit by the end of the class.

evaluations %>%
    ggplot() +
    aes(x = assignment, fill = submitted) +
    geom_bar(position = "fill") +
    scale_y_continuous(labels = percent_format()) +
    theme_minimal() +
    labs(
        fill = "Submitted",
        y = NULL,
        x = "Number of assignment",
        title = "Percentage of submitted assignments",
        subtitle = "The number of unsubmitted assignment did not really change over time"
    )

Points for the assignments

The distibution is bimodal, because those who did not submit a particular assignmen received zero points. Otherwise, the distributions seem rather normal. There is a small difference between assignments. (I admit that this is not the best presentation of the data, as it suggests that there are points below 0% and points above 100%, but it looks so nice!)

evaluations %>%
    ggplot() +
    aes(x = rel_points, y = assignment, fill = assignment) +
    geom_density_ridges2(alpha = .8) +
    scale_x_continuous(labels = percent_format()) +
    theme_minimal() +
    labs(
        y = NULL,
        x = NULL,
        title = "Distribution of the relative performance for each assignment",
        subtitle = "It seems like you did the best job for the last assignment (also you had more time)"
    )

Learning trajectories for each student

I calculated whether students’ performance improved, deteriorated, or remained similar over time using a linear regression slope. I plot this info as color in the plot, where the relative performance is also shown.

# Calculate regression for each student, to find if the performance was increasing, decreasing, or stable
trajectory <- 
    evaluations %>% 
    group_by(email) %>% 
    do(slope = lm(rel_points ~ as.numeric(assignment), data = .)$coef[2]) %>% 
    unnest() %>% 
    mutate(performance = case_when(slope < -.05 ~ "Decreasing",
                                   slope > .05 ~ "Increasing",
                                   TRUE ~ "Stable") %>% as.factor())
set.seed(42) # I will put 3% of random noise to the data, so lines can be seen separately
evaluations %>%
    left_join(trajectory, by = "email") %>% 
    ggplot() +
    aes(x = assignment, y = rel_points, group = email, color = performance, alpha = I(.6)) +
    geom_line(
        arrow = arrow(type = "open"),
        linejoin = "round",
        size = 2,
        position = position_jitter(width = 0, height = .03)) +
    scale_y_continuous(labels = percent_format()) +
    theme_minimal() +
    labs(y = NULL, 
         title = "Trajectories of student performances across three assignments",
         subtitle = "(Mind the 3% vertical noise in the data so arrows don't overlap)")

Final points

Here is the distibution of final points. 100 was the max, and everyone got an extra 20 points for participation, because I’m kind (actually, that’s because I was lazy to set explicit rules, and did not document participation).

# Summarised points (+20 for participation) --------------------------------
evaluations %>% 
    # filter(points > 0) %>% 
    group_by(email) %>% 
    summarise(sum_points = sum(points, na.rm = TRUE) + 20) %>% 
    ggplot() +
        aes(x = sum_points) +
        geom_density(fill = "blue", alpha = .5) +
    theme_minimal()

Comment mining

I have also given you feedback on all assignments. Let’s see how many comments there are!

# Prepare the comments for visualizations
comments <- 
    evaluations %>% 
    filter(points != 0) %>% 
    group_by(assignment, email) %>% 
    summarise(text = paste(`comment on outputs`, `comment on code`, `comment on report`, collapse =" ")) %>% 
    unnest_tokens(word, text) %>% 
    filter(word != "na") %>% 
    ungroup()

To protect personality rights, I remove most of the email address.

comments %>% 
    group_by(assignment, email) %>% 
    count() %>% 
    ungroup() %>% 
    mutate(email = paste0(str_sub(email, 0,3),"...@...", str_sub(email, -2,-1))) %>% 
        ggplot() +
            aes(y = n, fill = fct_rev(assignment), x = fct_rev(email), label = n) +
            geom_col(alpha = .9) + 
            geom_label(position = "stack") +
            coord_flip() +
            theme_minimal() +
            labs(x = NULL, y = "Number of words in the feedback for each assignment", 
                 title = "Number of words in the feedback by assignment",
                 subtitle = "Only for those who submitted an assignment",
                 fill = "Assignment")

Here is a fancy wordcloud from my comments

What are the most frequent words that I used in the feedbacks?

# Fancy wordcloud
comments %>% 
    anti_join(stop_words, by = "word") %>% 
    count(word, sort = TRUE) %>% 
    wordcloud2()

<

<

Jesus, I’m boring!!!

But am I at least kind?

It seems like. I’ve given surprisingly few negative comments 👼

# Using proportion of answers with a particular sentiment
comments %>% 
    anti_join(stop_words, by = "word") %>% 
    left_join(get_sentiments("nrc"), by = "word") %>% 
    group_by(assignment) %>% 
    mutate(all_comment = n()) %>% 
    group_by(assignment, sentiment) %>% 
    summarise(n = n(),
              perc = n()/first(all_comment)) %>% 
    drop_na(sentiment) %>% 
    ggplot() +
        aes(x = sentiment, y = perc, fill = sentiment, label = perc %>% round(2) %>% percent()) +
        guides(fill = FALSE) +
        geom_col() +
        geom_label(position = "stack") +
        theme_minimal() +
        facet_wrap(~assignment, labeller = label_both) +
        scale_y_continuous(labels = percent_format()) +
        coord_flip() +
        labs(x = NULL, y = NULL, 
             title = "The proportion of words associated with a specific emotion")

Don’t go just yet! Please fill the minimalist feedback questionnaire until 5th of February!!!!

Now, go out to the word and spread the word about #Rstats!

Also, you can fast your eyes on the beautiful assignments that the others made:

git_data <- 
    gs_title('Final assignment for "R programming for reproducible research"  (Responses)', verbose = FALSE) %>% 
    gs_read(1, verbose = FALSE)
git_data %>% 
    select(html)
LS0tDQp0aXRsZTogIkV2YWx1YXRpbmcgdGhlIHBlcmZvcm1hbmNlIG9mIHN0dWRlbnRzIGZvciAnUHJvZ3JhbW1pbmcgaW4gUiBmb3IgcmVwcm9kdWNpYmxlIHJlc2VhcmNoJyINCm91dHB1dDogDQogICAgaHRtbF9ub3RlYm9vazoNCiAgICAgICAgdGhlbWU6IHNhbmRzdG9uZQ0KLS0tDQoNCiMgRXhlY3V0aXZlIHN1bW1hcnkNCkkgd2FudCB0byBnaXZlIHlvdSBhIGZpbmFsIGdpZnQsIHNvIEkgcHV0IHRvZ2V0aGVyIGEgbGl0dGUgYW5hbHlzaXMgb2YgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBncm91cC4NCkluIHRoaXMgbm90ZWJvb2ssIEkgd2lsbCBzaG93IGhvdyB5b3UgcGVyZm9ybWVkIGR1cmluZyB0aGUgY291cnNlLCBhbmQgd2hhdCBraW5kIG9mIGZlZWRiYWNrIHlvdSByZWNlaXZlZCBmcm9tIG1lLiBEb24ndCB3b3JyeSwgbm8gb25lIGlzIHNpbmdsZWQgb3V0IGluIHRoZSBhbmFseXNpcywgYW5kIGlkZW50aXRpZXMgYXJlIGtlcHQgaW4gY29uZmlkZW5jZS4NCg0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGdvb2dsZXNoZWV0cykNCmxpYnJhcnkoZ2dyaWRnZXMpDQpsaWJyYXJ5KHNjYWxlcykNCmxpYnJhcnkodGlkeXRleHQpDQpsaWJyYXJ5KHdvcmRjbG91ZDIpDQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImhhZGxleS9lbW8iKQ0KbGlicmFyeShlbW8pDQpgYGANCg0KSSdtIGRvd25sb2FkaW5nIHRoZSBkYXRhIGRpcmVjdGx5IGZyb20gZ29vZ2xlIHNoZWV0cy4gVG8gcHJvdGVjdCBwZXJzb25hbCByaWdodHMsIHRoZSBkYXRhIGlzIG5vdCBwdWJsaWMgYHIgZW1vOjpqaSgic2VjcmV0IilgLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBSZWFkIHRoZSBmaWxlcyBkaXJlY3RseSBmcm9tIGdvb2dsZSBzaGVldHMgYW5kIGFkZCB0aGUgbnVtYmVyIG9mIHRoZSBldmFsdWF0aW9uIChhcyBjaGFyYWN0ZXIpDQpldmFsMSA8LQ0KICAgIGdzX3RpdGxlKCJhc3NpZ25tZW50MV9ldmFsdWF0aW9ucyIsIHZlcmJvc2UgPSBGQUxTRSkgJT4lIA0KICAgIGdzX3JlYWQoMSwgdmVyYm9zZSA9IEZBTFNFKSAlPiUgDQogICAgbXV0YXRlKGFzc2lnbm1lbnQgPSAiMSIpDQpldmFsMiA8LQ0KICAgIGdzX3RpdGxlKCJhc3NpZ25tZW50Ml9ldmFsdWF0aW9ucyIsIHZlcmJvc2UgPSBGQUxTRSkgJT4lIA0KICAgIGdzX3JlYWQoMSwgdmVyYm9zZSA9IEZBTFNFKSAlPiUgDQogICAgbXV0YXRlKGFzc2lnbm1lbnQgPSAiMiIpDQpldmFsMyA8LQ0KICAgIGdzX3RpdGxlKCJhc3NpZ25tZW50M19ldmFsdWF0aW9ucyIsIHZlcmJvc2UgPSBGQUxTRSkgJT4lIA0KICAgIGdzX3JlYWQoMSwgdmVyYm9zZSA9IEZBTFNFKSAlPiUgDQogICAgbXV0YXRlKGFzc2lnbm1lbnQgPSAiMyIpDQoNCiMgQmluZCB0aGUgdGhyZWUgZXZhbHVhdGlvbnMgdG9nZXRoZXINCmV2YWx1YXRpb25zIDwtIA0KICAgIGJpbmRfcm93cyhldmFsMSwgZXZhbDIsIGV2YWwzKSAlPiUgDQogICAgbXV0YXRlKCBhc3NpZ25tZW50ID0gYXMuZmFjdG9yKGFzc2lnbm1lbnQpLA0KICAgICAgICAgICAgc3VibWl0dGVkID0gaWZfZWxzZShwb2ludHMgIT0gMCwgVFJVRSwgRkFMU0UpLA0KICAgICAgICAgICAgcmVsX3BvaW50cyA9IHBvaW50cy9tYXhfcG9pbnRzKQ0KYGBgDQoNCiMgU29ycnksIEkgY2FuJ3QgZ2l2ZSB5b3UgdGhlIGRhdGEhIGByIGVtbzo6amkoInNhZCIpYA0KDQo8Y2VudGVyPiAhW10oaHR0cHM6Ly9tZWRpYS5naXBoeS5jb20vbWVkaWEvdExHQnluTGJvVVRMeS9naXBoeS5naWYpIDwvY2VudGVyPg0KDQojIFBlcmNlbnRhZ2Ugb2Ygc3VibWl0dGVkIGFzc2lnbm1lbnRzDQpGaXJzdCwgbGV0J3Mgc2VlIGhvdyBtYW55IG9mIHlvdSB3ZXJlIGJ1c3kgYmVlcyBhbmQgc3VibWl0dGVkIGFzc2lnbm1lbnRzLiBJdCBsb29rcyBsaWtlIG1vc3Qgb2YgeW91IHdobyBzdWJtaXR0ZWQgYW4gYXNzaWdubWVudCBrZXB0IHRoaXMgZ29vZCBoYWJpdCBieSB0aGUgZW5kIG9mIHRoZSBjbGFzcy4NCg0KYGBge3IgZmlnLmFsaWduPSJjZW50ZXIiLCBmaWcud2lkdGg9MTB9DQpldmFsdWF0aW9ucyAlPiUNCiAgICBnZ3Bsb3QoKSArDQogICAgYWVzKHggPSBhc3NpZ25tZW50LCBmaWxsID0gc3VibWl0dGVkKSArDQogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudF9mb3JtYXQoKSkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgbGFicygNCiAgICAgICAgZmlsbCA9ICJTdWJtaXR0ZWQiLA0KICAgICAgICB5ID0gTlVMTCwNCiAgICAgICAgeCA9ICJOdW1iZXIgb2YgYXNzaWdubWVudCIsDQogICAgICAgIHRpdGxlID0gIlBlcmNlbnRhZ2Ugb2Ygc3VibWl0dGVkIGFzc2lnbm1lbnRzIiwNCiAgICAgICAgc3VidGl0bGUgPSAiVGhlIG51bWJlciBvZiB1bnN1Ym1pdHRlZCBhc3NpZ25tZW50IGRpZCBub3QgcmVhbGx5IGNoYW5nZSBvdmVyIHRpbWUiDQogICAgKQ0KYGBgDQoNCiMgUG9pbnRzIGZvciB0aGUgYXNzaWdubWVudHMNCg0KVGhlIGRpc3RpYnV0aW9uIGlzIGJpbW9kYWwsIGJlY2F1c2UgdGhvc2Ugd2hvIGRpZCBub3Qgc3VibWl0IGEgcGFydGljdWxhciBhc3NpZ25tZW4gcmVjZWl2ZWQgemVybyBwb2ludHMuIE90aGVyd2lzZSwgdGhlIGRpc3RyaWJ1dGlvbnMgc2VlbSByYXRoZXIgbm9ybWFsLiBUaGVyZSBpcyBhIHNtYWxsIGRpZmZlcmVuY2UgYmV0d2VlbiBhc3NpZ25tZW50cy4NCihJIGFkbWl0IHRoYXQgdGhpcyBpcyBub3QgdGhlIGJlc3QgcHJlc2VudGF0aW9uIG9mIHRoZSBkYXRhLCBhcyBpdCBzdWdnZXN0cyB0aGF0IHRoZXJlIGFyZSBwb2ludHMgYmVsb3cgMCUgYW5kIHBvaW50cyBhYm92ZSAxMDAlLCBidXQgaXQgbG9va3Mgc28gbmljZSEpDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5hbGlnbj0iY2VudGVyIiwgZmlnLndpZHRoPTEwfQ0KZXZhbHVhdGlvbnMgJT4lDQogICAgZ2dwbG90KCkgKw0KICAgIGFlcyh4ID0gcmVsX3BvaW50cywgeSA9IGFzc2lnbm1lbnQsIGZpbGwgPSBhc3NpZ25tZW50KSArDQogICAgZ2VvbV9kZW5zaXR5X3JpZGdlczIoYWxwaGEgPSAuOCkgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdCgpKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBsYWJzKA0KICAgICAgICB5ID0gTlVMTCwNCiAgICAgICAgeCA9IE5VTEwsDQogICAgICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiB0aGUgcmVsYXRpdmUgcGVyZm9ybWFuY2UgZm9yIGVhY2ggYXNzaWdubWVudCIsDQogICAgICAgIHN1YnRpdGxlID0gIkl0IHNlZW1zIGxpa2UgeW91IGRpZCB0aGUgYmVzdCBqb2IgZm9yIHRoZSBsYXN0IGFzc2lnbm1lbnQgKGFsc28geW91IGhhZCBtb3JlIHRpbWUpIg0KICAgICkNCmBgYA0KDQojIyBMZWFybmluZyB0cmFqZWN0b3JpZXMgZm9yIGVhY2ggc3R1ZGVudA0KSSBjYWxjdWxhdGVkIHdoZXRoZXIgc3R1ZGVudHMnIHBlcmZvcm1hbmNlIGltcHJvdmVkLCBkZXRlcmlvcmF0ZWQsIG9yIHJlbWFpbmVkIHNpbWlsYXIgb3ZlciB0aW1lIHVzaW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gc2xvcGUuIEkgcGxvdCB0aGlzIGluZm8gYXMgY29sb3IgaW4gdGhlIHBsb3QsIHdoZXJlIHRoZSByZWxhdGl2ZSBwZXJmb3JtYW5jZSBpcyBhbHNvIHNob3duLg0KDQpgYGB7ciBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy53aWR0aD0xMCwgd2FybmluZz1GQUxTRX0NCiMgQ2FsY3VsYXRlIHJlZ3Jlc3Npb24gZm9yIGVhY2ggc3R1ZGVudCwgdG8gZmluZCBpZiB0aGUgcGVyZm9ybWFuY2Ugd2FzIGluY3JlYXNpbmcsIGRlY3JlYXNpbmcsIG9yIHN0YWJsZQ0KdHJhamVjdG9yeSA8LSANCiAgICBldmFsdWF0aW9ucyAlPiUgDQogICAgZ3JvdXBfYnkoZW1haWwpICU+JSANCiAgICBkbyhzbG9wZSA9IGxtKHJlbF9wb2ludHMgfiBhcy5udW1lcmljKGFzc2lnbm1lbnQpLCBkYXRhID0gLikkY29lZlsyXSkgJT4lIA0KICAgIHVubmVzdCgpICU+JSANCiAgICBtdXRhdGUocGVyZm9ybWFuY2UgPSBjYXNlX3doZW4oc2xvcGUgPCAtLjA1IH4gIkRlY3JlYXNpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbG9wZSA+IC4wNSB+ICJJbmNyZWFzaW5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJTdGFibGUiKSAlPiUgYXMuZmFjdG9yKCkpDQoNCnNldC5zZWVkKDQyKSAjIEkgd2lsbCBwdXQgMyUgb2YgcmFuZG9tIG5vaXNlIHRvIHRoZSBkYXRhLCBzbyBsaW5lcyBjYW4gYmUgc2VlbiBzZXBhcmF0ZWx5DQpldmFsdWF0aW9ucyAlPiUNCiAgICBsZWZ0X2pvaW4odHJhamVjdG9yeSwgYnkgPSAiZW1haWwiKSAlPiUgDQogICAgZ2dwbG90KCkgKw0KICAgIGFlcyh4ID0gYXNzaWdubWVudCwgeSA9IHJlbF9wb2ludHMsIGdyb3VwID0gZW1haWwsIGNvbG9yID0gcGVyZm9ybWFuY2UsIGFscGhhID0gSSguNikpICsNCiAgICBnZW9tX2xpbmUoDQogICAgICAgIGFycm93ID0gYXJyb3codHlwZSA9ICJvcGVuIiksDQogICAgICAgIGxpbmVqb2luID0gInJvdW5kIiwNCiAgICAgICAgc2l6ZSA9IDIsDQogICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMCwgaGVpZ2h0ID0gLjAzKSkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdCgpKSArDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBsYWJzKHkgPSBOVUxMLCANCiAgICAgICAgIHRpdGxlID0gIlRyYWplY3RvcmllcyBvZiBzdHVkZW50IHBlcmZvcm1hbmNlcyBhY3Jvc3MgdGhyZWUgYXNzaWdubWVudHMiLA0KICAgICAgICAgc3VidGl0bGUgPSAiKE1pbmQgdGhlIDMlIHZlcnRpY2FsIG5vaXNlIGluIHRoZSBkYXRhIHNvIGFycm93cyBkb24ndCBvdmVybGFwKSIpDQoNCmBgYA0KDQo8Y2VudGVyPiFbXShodHRwczovL21lZGlhLmdpcGh5LmNvbS9tZWRpYS9yZHdBRDMyWW5nMTZvL2dpcGh5LmdpZik8L2NlbnRlcj4NCg0KIyMgRmluYWwgcG9pbnRzDQoNCkhlcmUgaXMgdGhlIGRpc3RpYnV0aW9uIG9mIGZpbmFsIHBvaW50cy4gMTAwIHdhcyB0aGUgbWF4LCBhbmQgZXZlcnlvbmUgZ290IGFuIGV4dHJhIDIwIHBvaW50cyBmb3IgcGFydGljaXBhdGlvbiwgYmVjYXVzZSBJJ20ga2luZCAoYWN0dWFsbHksIHRoYXQncyBiZWNhdXNlIEkgd2FzIGxhenkgdG8gc2V0IGV4cGxpY2l0IHJ1bGVzLCBhbmQgZGlkIG5vdCBkb2N1bWVudCBwYXJ0aWNpcGF0aW9uKS4NCg0KPGNlbnRlcj4gIVtdKGh0dHBzOi8vbWVkaWEuZ2lwaHkuY29tL21lZGlhLzNvODV4d3hyMDZZTm9GZFNibS9naXBoeS5naWYpIDwvY2VudGVyPg0KDQpgYGB7ciBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy5oZWlnaHQ9Nn0NCiMgU3VtbWFyaXNlZCBwb2ludHMgKCsyMCBmb3IgcGFydGljaXBhdGlvbikgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmV2YWx1YXRpb25zICU+JSANCiAgICAjIGZpbHRlcihwb2ludHMgPiAwKSAlPiUgDQogICAgZ3JvdXBfYnkoZW1haWwpICU+JSANCiAgICBzdW1tYXJpc2Uoc3VtX3BvaW50cyA9IHN1bShwb2ludHMsIG5hLnJtID0gVFJVRSkgKyAyMCkgJT4lIA0KICAgIGdncGxvdCgpICsNCiAgICAgICAgYWVzKHggPSBzdW1fcG9pbnRzKSArDQogICAgICAgIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IC41KSArDQogICAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KIyBDb21tZW50IG1pbmluZw0KSSBoYXZlIGFsc28gZ2l2ZW4geW91IGZlZWRiYWNrIG9uIGFsbCBhc3NpZ25tZW50cy4gTGV0J3Mgc2VlIGhvdyBtYW55IGNvbW1lbnRzIHRoZXJlIGFyZSENCg0KYGBge3J9DQojIFByZXBhcmUgdGhlIGNvbW1lbnRzIGZvciB2aXN1YWxpemF0aW9ucw0KY29tbWVudHMgPC0gDQogICAgZXZhbHVhdGlvbnMgJT4lIA0KICAgIGZpbHRlcihwb2ludHMgIT0gMCkgJT4lIA0KICAgIGdyb3VwX2J5KGFzc2lnbm1lbnQsIGVtYWlsKSAlPiUgDQogICAgc3VtbWFyaXNlKHRleHQgPSBwYXN0ZShgY29tbWVudCBvbiBvdXRwdXRzYCwgYGNvbW1lbnQgb24gY29kZWAsIGBjb21tZW50IG9uIHJlcG9ydGAsIGNvbGxhcHNlID0iICIpKSAlPiUgDQogICAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KSAlPiUgDQogICAgZmlsdGVyKHdvcmQgIT0gIm5hIikgJT4lIA0KICAgIHVuZ3JvdXAoKQ0KYGBgDQoNClRvIHByb3RlY3QgcGVyc29uYWxpdHkgcmlnaHRzLCBJIHJlbW92ZSBtb3N0IG9mIHRoZSBlbWFpbCBhZGRyZXNzLg0KDQpgYGB7ciBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy5oZWlnaHQ9OH0NCmNvbW1lbnRzICU+JSANCiAgICBncm91cF9ieShhc3NpZ25tZW50LCBlbWFpbCkgJT4lIA0KICAgIGNvdW50KCkgJT4lIA0KICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgbXV0YXRlKGVtYWlsID0gcGFzdGUwKHN0cl9zdWIoZW1haWwsIDAsMyksIi4uLkAuLi4iLCBzdHJfc3ViKGVtYWlsLCAtMiwtMSkpKSAlPiUgDQogICAgICAgIGdncGxvdCgpICsNCiAgICAgICAgICAgIGFlcyh5ID0gbiwgZmlsbCA9IGZjdF9yZXYoYXNzaWdubWVudCksIHggPSBmY3RfcmV2KGVtYWlsKSwgbGFiZWwgPSBuKSArDQogICAgICAgICAgICBnZW9tX2NvbChhbHBoYSA9IC45KSArIA0KICAgICAgICAgICAgZ2VvbV9sYWJlbChwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgICAgICAgICAgIGNvb3JkX2ZsaXAoKSArDQogICAgICAgICAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgICAgICAgbGFicyh4ID0gTlVMTCwgeSA9ICJOdW1iZXIgb2Ygd29yZHMgaW4gdGhlIGZlZWRiYWNrIGZvciBlYWNoIGFzc2lnbm1lbnQiLCANCiAgICAgICAgICAgICAgICAgdGl0bGUgPSAiTnVtYmVyIG9mIHdvcmRzIGluIHRoZSBmZWVkYmFjayBieSBhc3NpZ25tZW50IiwNCiAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiT25seSBmb3IgdGhvc2Ugd2hvIHN1Ym1pdHRlZCBhbiBhc3NpZ25tZW50IiwNCiAgICAgICAgICAgICAgICAgZmlsbCA9ICJBc3NpZ25tZW50IikNCg0KYGBgDQoNCg0KIyBIZXJlIGlzIGEgZmFuY3kgd29yZGNsb3VkIGZyb20gbXkgY29tbWVudHMNCldoYXQgYXJlIHRoZSBtb3N0IGZyZXF1ZW50IHdvcmRzIHRoYXQgSSB1c2VkIGluIHRoZSBmZWVkYmFja3M/IA0KDQpgYGB7ciBmaWcuYWxpZ249ICJjZW50ZXIiLCBmaWcud2lkdGg9MTB9DQojIEZhbmN5IHdvcmRjbG91ZA0KY29tbWVudHMgJT4lIA0KICAgIGFudGlfam9pbihzdG9wX3dvcmRzLCBieSA9ICJ3b3JkIikgJT4lIA0KICAgIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUgDQogICAgd29yZGNsb3VkMigpDQpgYGANCg0KDQoNCiMgSmVzdXMsIEknbSBib3JpbmchISENCg0KPGNlbnRlcj4hW10oaHR0cHM6Ly9tZWRpYS5naXBoeS5jb20vbWVkaWEvNWdVbk9ybHRQdlp6Vy9naXBoeS5naWYpPC9jZW50ZXI+DQoNCiMgQnV0IGFtIEkgYXQgbGVhc3Qga2luZD8NCg0KSXQgc2VlbXMgbGlrZS4gSSd2ZSBnaXZlbiBzdXJwcmlzaW5nbHkgZmV3IG5lZ2F0aXZlIGNvbW1lbnRzIGByIGVtbzo6amkoImFuZ2VsIilgDQoNCmBgYHtyIGZpZy5hbGlnbj0gImNlbnRlciIsIGZpZy53aWR0aD0xMH0NCiMgVXNpbmcgcHJvcG9ydGlvbiBvZiBhbnN3ZXJzIHdpdGggYSBwYXJ0aWN1bGFyIHNlbnRpbWVudA0KY29tbWVudHMgJT4lIA0KICAgIGFudGlfam9pbihzdG9wX3dvcmRzLCBieSA9ICJ3b3JkIikgJT4lIA0KICAgIGxlZnRfam9pbihnZXRfc2VudGltZW50cygibnJjIiksIGJ5ID0gIndvcmQiKSAlPiUgDQogICAgZ3JvdXBfYnkoYXNzaWdubWVudCkgJT4lIA0KICAgIG11dGF0ZShhbGxfY29tbWVudCA9IG4oKSkgJT4lIA0KICAgIGdyb3VwX2J5KGFzc2lnbm1lbnQsIHNlbnRpbWVudCkgJT4lIA0KICAgIHN1bW1hcmlzZShuID0gbigpLA0KICAgICAgICAgICAgICBwZXJjID0gbigpL2ZpcnN0KGFsbF9jb21tZW50KSkgJT4lIA0KICAgIGRyb3BfbmEoc2VudGltZW50KSAlPiUgDQogICAgZ2dwbG90KCkgKw0KICAgICAgICBhZXMoeCA9IHNlbnRpbWVudCwgeSA9IHBlcmMsIGZpbGwgPSBzZW50aW1lbnQsIGxhYmVsID0gcGVyYyAlPiUgcm91bmQoMikgJT4lIHBlcmNlbnQoKSkgKw0KICAgICAgICBndWlkZXMoZmlsbCA9IEZBTFNFKSArDQogICAgICAgIGdlb21fY29sKCkgKw0KICAgICAgICBnZW9tX2xhYmVsKHBvc2l0aW9uID0gInN0YWNrIikgKw0KICAgICAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgICAgICBmYWNldF93cmFwKH5hc3NpZ25tZW50LCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHBlcmNlbnRfZm9ybWF0KCkpICsNCiAgICAgICAgY29vcmRfZmxpcCgpICsNCiAgICAgICAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwsIA0KICAgICAgICAgICAgIHRpdGxlID0gIlRoZSBwcm9wb3J0aW9uIG9mIHdvcmRzIGFzc29jaWF0ZWQgd2l0aCBhIHNwZWNpZmljIGVtb3Rpb24iKQ0KYGBgDQoNCg0KIyBEb24ndCBnbyBqdXN0IHlldCEgUGxlYXNlIGZpbGwgdGhlIG1pbmltYWxpc3QgW2ZlZWRiYWNrIHF1ZXN0aW9ubmFpcmVdKGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL2Zvcm1zL2QvZS8xRkFJcFFMU2MzZ3FpSElCbkJlX3BVTEtBQWVnREZITmlwWHRzTjJJRVNReWYyVlJBcVZ2QkxwUS92aWV3Zm9ybT91c3A9c2ZfbGluaykgdW50aWwgNXRoIG9mIEZlYnJ1YXJ5ISEhIQ0KDQpOb3csIGdvIG91dCB0byB0aGUgd29yZCBhbmQgc3ByZWFkIHRoZSB3b3JkIGFib3V0ICNSc3RhdHMhDQoNCiMjQWxzbywgeW91IGNhbiBmYXN0IHlvdXIgZXllcyBvbiB0aGUgYmVhdXRpZnVsIGFzc2lnbm1lbnRzIHRoYXQgdGhlIG90aGVycyBtYWRlOg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZ2l0X2RhdGEgPC0gDQogICAgZ3NfdGl0bGUoJ0ZpbmFsIGFzc2lnbm1lbnQgZm9yICJSIHByb2dyYW1taW5nIGZvciByZXByb2R1Y2libGUgcmVzZWFyY2giICAoUmVzcG9uc2VzKScsIHZlcmJvc2UgPSBGQUxTRSkgJT4lIA0KICAgIGdzX3JlYWQoMSwgdmVyYm9zZSA9IEZBTFNFKQ0KDQpnaXRfZGF0YSAlPiUgDQogICAgc2VsZWN0KGh0bWwpDQpgYGANCg0KDQo8Y2VudGVyPiAhW10oaHR0cHM6Ly9tZWRpYS5naXBoeS5jb20vbWVkaWEvdXBnMGkxbTRETGU1cS9naXBoeS5naWYpIDwvY2VudGVyPg0KDQoNCg==